/*
 * Copyright 2007 Dan Hodge
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *      http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package net.datajanitor.baja.spi.support;

import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.Serializable;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

/**
 * A Map implementation that will load/store all key-value pairs from/to a disk-based
 * cache file using Java serialization. 
 */
public class PersistentMap<K extends Serializable, V extends Serializable> implements Map<K,V> {
    private static final long serialVersionUID = -6087281744513599616L;
    private Map<K, V> map;
    private File file;
    
    private void write() {
        ObjectOutputStream outs = null;
        try {
            outs = new ObjectOutputStream(new FileOutputStream(this.file));
            outs.writeObject(this.map);
        } catch (IOException e) {
            // throw as runtime
        } finally {
            if (outs != null) {
                try {
                    outs.close();
                } catch (IOException ignore) {
                }
            }
        }
    }
    
    private void read() {
        ObjectInputStream ins = null;
        
        try {
            ins = new ObjectInputStream(new FileInputStream(this.file));
    
            Object obj = ins.readObject();
            this.map = (Map<K, V>) obj;
        } catch (Exception e) {
            
        } finally {
            if (ins != null) {
                try {
                    ins.close();
                } catch (IOException ignore) {
                }
            }
        }        
    }
    
    public PersistentMap(File file) {
        this.file = file;
        this.map = new HashMap<K,V>();
        if (this.file.exists()) {
            read();
        }
    }
    
    public synchronized void clear() {
        this.map.clear();
        write();
    }

    public synchronized boolean containsKey(Object key) {
        return this.map.containsKey(key);
    }

    public synchronized boolean containsValue(Object value) {
        return this.map.containsValue(value);
    }

    public synchronized Set<Map.Entry<K, V>> entrySet() {
        return this.map.entrySet();
    }

    public synchronized V get(Object key) {
        return this.map.get(key);
    }

    public synchronized boolean isEmpty() {
        return this.map.isEmpty();
    }

    public synchronized Set<K> keySet() {
        return this.map.keySet();
    }

    public synchronized V put(K key, V value) {
        V val = this.map.put(key, value);
        write();
        
        return val;
    }

    public synchronized void putAll(Map<? extends K, ? extends V> t) {
        this.map.putAll(t);
        write();        
    }

    public synchronized V remove(Object key) {
        return this.map.remove(key);    
    }

    public synchronized int size() {
        return this.map.size();
    }

    public synchronized Collection<V> values() {
        return this.map.values();
    }
}
